<template>
    <div
        id="scroll_container"
        ref="scrollContainer"
        @mousemove="movingScroller"
        @touchstart="touchStart"
        @touchmove="touchMoveScroller"
        :style="{ '--transformX': -smoothOffsetX.toFixed(1), '--content-margin': marginHorizontal }"
    >
        <div class="mask"></div>
        <div id="scroll_content" ref="scrollContent">
            <slot></slot>
        </div>
    </div>
</template>

<!--
    该容器组件可以通过鼠标的 hover 来滚动内部的内容，
    且可在移动设备上直接通过滑动来滚动内容
-->
<script>
import {reactive, ref, toRefs} from "vue"
import SafeCall from "../utils/SafeCall"

export default {
    props: {
        marginHorizontal: {
            type: String,
            default: '64px'
        },
        maskPercent: {
            type: String,
            default: '10%'
        },
        getPercent: Function
    },
    setup(props) {
        const scrollContainer = ref(null)
        const scrollContent = ref(null)
        const data = reactive({
            touchDownX: 0,
            offsetX: 0,
            offsetXPercent: 0,
            smoothOffsetX: 0,
            touching: false,
        })

        const clamp = (min, max, value) => Math.max(min, Math.min(max, value))

        // 处理 mousemove 鼠标滑动的事件
        const movingScroller = (ev) => {
            // 阻止 touchstart 触发时附带触发的 mousemove 事件
            if (data.touching) {
                data.touching = false
                return
            }

            let clientWidth = scrollContent.value?.clientWidth
            let scrollWidth = scrollContent.value?.scrollWidth
            let x = ev.clientX - scrollContainer.value?.offsetLeft
            let xPercent = clamp(0, 1, (x / clientWidth - 0.5) / 0.6 + 0.5)
            data.offsetX = (scrollWidth - clientWidth) * xPercent
            SafeCall(props.getPercent, xPercent)
            ev.preventDefault()
        }

        // 处理 touchmove 触摸滑动的事件
        const touchMoveScroller = (ev) => {
            let clientWidth = scrollContent.value?.clientWidth
            let scrollWidth = scrollContent.value?.scrollWidth
            let movementX = data.touchDownX - ev.touches[0].clientX
            data.offsetX = clamp(0, scrollWidth - clientWidth, data.offsetX + movementX * 2)
            data.touchDownX = ev.touches[0].clientX
            ev.preventDefault()
        }

        const touchStart = (ev) => {
            data.touching = true
            data.touchDownX = ev.touches[0].clientX
        }

        // 非线性滚动的数据循环
        const scrollRecycle = () => {
            data.smoothOffsetX += (data.offsetX - data.smoothOffsetX) * 0.1
            window?.requestAnimationFrame(scrollRecycle)
        }
        scrollRecycle()

        return {
            ...toRefs(data),
            movingScroller,
            touchStart,
            touchMoveScroller,
            scrollContainer,
            scrollContent
        }
    }
}
</script>

<style scoped lang="less">
#scroll_container {
    --transformX: 0;
    --content-margin: 64px;
    --mask-percent: 5%;
    --mask-color: rgb(29, 29, 29);
    --mask-linear-gradient: linear-gradient(
        90deg,
        black 0%,
        transparent var(--mask-percent),
        transparent calc(100% - var(--mask-percent)),
        black 100%
    );

    position: relative;
    overflow: hidden;
    transform-style: preserve-3d;

    & > .mask {
      z-index: 1;
      position: absolute;
      width: 100%;
      height: 100%;
      pointer-events: none;
      -webkit-mask-image: var(--mask-linear-gradient);
      background-color: var(--mask-color);
      mask-image: var(--mask-linear-gradient);
    }

    & > #scroll_content {
      margin: 0 var(--content-margin);
      transform: matrix(1, 0, 0, 1, var(--transformX), 0);
    }
}
</style>